#pragma once

#include <iostream>
#include <cstring>
#include <string>
#include <signal.h>

#include "socket.hpp"
#include "log_pro.hpp"
#include "calServer.hpp"

class tcpserver
{
public:
    tcpserver(uint16_t port)
        : _port(port)
    {
        init();
        lg(DEBUG, "init succese !");
    }

    ~tcpserver()
    {
    }

    void init()
    {
        _listenSock.Bind(_port);
        _listenSock.Listen();
    }

    void run()
    {
        lg(DEBUG, "tcp server runing !");
        // 忽略两个信号一个是为了保护进程寿命，另一个是让父进程不需要阻塞等待
        signal(SIGCHLD, SIG_IGN);
        signal(SIGPIPE, SIG_IGN);

        calserver cal; // 创建服务方法
        while (true)
        {
            std::string clientIp;
            uint16_t clientPort;
            int serverSock = _listenSock.Accept(clientIp, &clientPort);
            if (serverSock == -1)
                continue;
            lg(DEBUG, "accept success !");

            // 让子进程进行数据处理，父进程继续循环accept
            if (fork() == 0)
            {
                _listenSock.Close(); // 子进程不需要监听
                std::string getMassage;
                while (true) // 长服务
                {
                    char buffer[128] = {0};
                    int n = read(serverSock, buffer, sizeof(buffer) - 1);
                    buffer[n] = '\0';
                    if (n == 0)
                    {
                        lg(FATAL, "server close socket ! erron:%d %s", errno, strerror(errno));
                        exit(-1);
                    }
                    getMassage += buffer;
                    //lg(DEBUG,"\n%s",getMassage.c_str());
                    while (true) // 这里要加循环的原因是，我们从socket中获取了一次数据，我们就将这一批数据先在循环中消费掉
                                 // 如果放在上层的大循环中消费的话，由于客户端可能一次写入很多报文给服务器，
                                 // 而处理和读在同一个循环中，一定会导致读一次才消费一次，子进程写的是多条报文，
                                 // 读取一次也可以读很多报文，但是读取一次只能消费一条报文，一定会导致报文无法被读取完
                                 // 子进程结束了写入会导致，read返回0，子进程还在写入会导致，read阻塞的读取，让消费卡住
                    {
                        std::string sendInfo = cal.caculate(getMassage);
                        if (sendInfo.empty())
                            break;
                        //lg(DEBUG, "\n%s", sendInfo.c_str());
                        int n = write(serverSock, sendInfo.c_str(), sendInfo.size() + 1);
                        if (n == -1)
                        {
                            std::cout << "write fail !" << std::endl;
                            std::cout << getMassage << std::endl;
                        }
                    }
                }
                exit(-1);
            }
            close(serverSock); // 父进程可以关闭服务socket
        }
    }

private:
    uint16_t _port;
    Socket _listenSock;
};
